package zyf.jmidifile;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.security.InvalidParameterException;
import java.util.Arrays;

class JMidiChunk implements Serializable {
    public static final byte[] HEAD_ID = new byte[]{'M', 'T', 'h', 'd'};
    public static final byte[] TRACK_ID = new byte[]{'M', 'T', 'r', 'k'};
    final byte[] id;
    int size;

    JMidiChunk(final byte[] data, int[] off) throws InvalidParameterException {
        int offset = off[0];
        if ((data.length - offset) < 8) {
            makeInvalidMidiFileException();
        }

        id = Arrays.copyOfRange(data, offset, offset + 4);
        offset += 4;
        size = bytesToInt(data, offset, 4);
        offset += 4;

        if ((data.length - offset) < size) {
            makeInvalidMidiFileException();
        }
        off[0] = offset;
    }

    JMidiChunk(byte[] id, int size) {
        this.id = id;
        this.size = size;
    }

    JMidiChunk(byte[] id) {
        this.id = id;
    }

    void save(BufferedOutputStream stream) throws IOException {
        stream.write(id);
        stream.write(intToBytes(size, 4));
    }

    static void makeInvalidMidiFileException() throws InvalidParameterException {
        throw new InvalidParameterException("invalid midi file data");
    }

    static int bytesToInt(final byte[] data, int off, int len) {
        int num = 0;

        int shift = 0;
        for (int i = off + len - 1; i >= off; i--) {
            num += (data[i] & 0xff) << shift;
            shift += 8;
        }
        return num;
    }

    static byte[] intToBytes(int val, int byteCount, byte[] data, int off) {
        // 数据在 midi file 中按照大端字节序存储
        for (int i = 0; i < byteCount; ++i) {
            data[byteCount - i - 1 + off] = (byte) (val & 0xff);
            val = val >> 8;
        }
        return data;
    }

    static byte[] intToBytes(int val, byte[] data) {
        return intToBytes(val, data.length, data, 0);
    }

    static byte[] intToBytes(int val, int byteCount) {
        byte[] d = new byte[byteCount];
        return intToBytes(val, byteCount, d, 0);
    }

    // 表示 midi file 内部动态数据的类型
    static class Var {
        public int len; // 动态数据长度
        private byte[] cache = new byte[4];

        public int readValue(final byte[] data, int off, int lenLimit) {
            int t = 0;
            if (lenLimit > 4) {
                lenLimit = 4;
            }
            int offset;
            for (int i = 0; i < lenLimit; ++i) {
                offset = i + off;
                if ((data[offset] & 0x80) != 0) {
                    t = (t << 7) + (data[offset] & 0x7f);
                } else {
                    len = i + 1;
                    return (t << 7) + data[offset];
                }
            }
            // 读取到size上限都未解析出来，表示解析失败了

            len = 0;
            return -1;
        }

        public byte[] writeData(int value) {
            len = 1;
            cache[3] = (byte) (value & 0x7f);
            while ((value >>= 7) != 0) {
                ++len;
                cache[4 - len] = (byte) ((value & 0x7f) | 0x80);
            }
            return Arrays.copyOfRange(cache, 4 - len, 4);
        }
    }
}

